/*
 * MIT License
 *
 * Copyright (c) 2019 Mr.css
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package cn.seaboot.commons.core;

import org.jetbrains.annotations.NotNull;

import java.util.Map;
import java.util.StringTokenizer;

/**
 * 简单分词器
 *
 * @author Mr.css
 * @version 2020-08-10 14:12
 */
public class Tokenizer {
    private static final String BRACE_LEFT = "{";
    private static final String BRACE_RIGHT = "}";

    private Tokenizer() {
    }

    /**
     * 替换字符
     * <p>
     * 占位符限定 {}，占位符内部不写数据，功能类似于 log4j。
     * 存在优化空间，但是没必要，效果提升不明显。
     *
     * @param message 内容
     * @param args    参数
     * @return a String
     */
    public static String replace(@NotNull String message, Object... args) {
        if (args == null || args.length == 0) {
            return message;
        } else {
            int next, n = 0, len = message.length();
            StringBuilder sb = new StringBuilder(len);
            for (int i = 0; i < len; i++) {
                char ch = message.charAt(i);
                if (ch == '{') {
                    next = i + 1;
                    if (next < len && message.charAt(next) == '}') {
                        i++;
                        sb.append(args[n++]);
                    } else {
                        sb.append(ch);
                    }
                } else {
                    sb.append(ch);
                }
            }
            return sb.toString();
        }
    }

    /**
     * 替换字符 {field}
     * <p>
     * 花括号内部填写字段名。
     *
     * @param message 内容
     * @param map     参数
     * @return a String
     */
    public static String process(@NotNull String message, Map<String, ?> map) {
        StringTokenizer tokens = new StringTokenizer(message, "{}", true);
        StringBuilder res = new StringBuilder(message.length());
        boolean escaped = false;
        while (tokens.hasMoreTokens()) {
            String token = tokens.nextToken();
            if (!escaped && BRACE_LEFT.equals(token)) {
                escaped = true;
            } else if (escaped && BRACE_RIGHT.equals(token)) {
                escaped = false;
            } else if (!escaped) {
                res.append(token);
            } else {
                Object object = map.get(token);
                res.append(Converter.toString(object));
            }
        }
        return res.toString();
    }

    /**
     * 替换字符 {n}
     * <p>
     * 功能与 mybatis 历史版本功能一致，花括号中间填写序号，用 arr[index] 进行替换
     * <p>
     * E.G.:
     * <p>
     * String[] arr = {"a", "b"};
     * System.out.println(process("{0} test {1} test", arr));
     *
     * @param message 内容
     * @param arr     参数
     * @return a String
     */
    public static String process(@NotNull String message, Object... arr) {
        if (arr == null || arr.length == 0) {
            return message;
        }
        StringTokenizer tokens = new StringTokenizer(message, "{}", true);
        StringBuilder res = new StringBuilder(message.length());
        boolean escaped = false;
        while (tokens.hasMoreTokens()) {
            String token = tokens.nextToken();
            if (!escaped && "{".equals(token)) {
                escaped = true;
            } else if (escaped && "}".equals(token)) {
                escaped = false;
            } else if (!escaped) {
                res.append(token);
            } else {
                Object object = arr[Integer.parseInt(token)];
                res.append(Converter.toString(object));
            }
        }
        return res.toString();
    }
}
